<?php
declare (strict_types=1);

namespace app\common\command;

use app\common\model\Job as JobModel;
use app\common\model\JobCount as JobCountModel;
use app\event\app\ExchangeCoupon as ExchangeCouponEvent;
use app\event\app\Pdd;
use app\event\app\ThirdRecharge as ThirdRechargeEvent;
use app\event\app\Tbk;
use app\event\app\Tui;
use app\event\index\Share as ShareEvent;
use app\event\pay\PointOrder as PointOrderEvent;
use app\event\pos\PosOrder as PosOrderEvent;
use app\event\ucenter\User as UserEvent;
use app\event\ucenter\User;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use think\facade\App;
use think\facade\Cache;
use think\facade\Db;
use think\facade\Log;
use think\helper\Str;
use Workerman\Connection\TcpConnection;
use app\event\pos\Pos as PosEvent;
use Workerman\Lib\Timer;
use Workerman\Worker;
use app\event\store\Store as StoreEvent;
use app\event\pay\Bill as BillEvent;
use app\event\app\Jd as JdEvent;
use app\event\app\Pdd as PddEvent;

class Task extends Command
{

    protected $port = 2350;
    protected $host = '0.0.0.0';
    protected $protocol = 'websocket';
    protected $worker;
    protected $event = ['onWorkerStart', 'onConnect', 'onMessage', 'onClose', 'onError', 'onWebSocketConnect'];

    public function __set($name, $value)
    {
        $this->worker->$name = $value;
    }

    public function __call($method, $args)
    {
        call_user_func_array([$this->worker, $method], $args);
    }

    protected function configure()
    {
        // 指令配置
        $this->setName('convert')
            ->addArgument('action', Argument::OPTIONAL, "start|stop|restart|reload|status|connections", 'start')
            ->addOption('mode', 'm', Option::VALUE_OPTIONAL, 'Run the workerman server in daemon mode.')
            ->setDescription('执行任务');
    }

    protected function execute(Input $input, Output $output)
    {
        // 指令输出
        $this->output = $output;
//        $output->writeln('convert start');
        $action = $input->getArgument('action');
        $mode = $input->getOption('mode');
        // 重新构造命令行参数,以便兼容workerman的命令
        global $argv;
        $argv = [];
        array_unshift($argv, 'think', $action);
        if ($mode == 'd') {
            $argv[] = '-d';
        } else if ($mode == 'g') {
            $argv[] = '-g';
        }
        Worker::$pidFile = root_path() . 'runtime/task.pid';;
        $this->worker = new Worker($this->protocol . '://' . $this->host . ':' . $this->port);
// 启动N个进程
        $this->worker->count = 1;

        // 设置回调
        foreach ($this->event as $event) {
            if (method_exists($this, $event)) {
                $this->worker->$event = [$this, $event];
            }
        }
        Worker::runAll();
    }

    public function onWorkerReload($worker)
    {
        // 重启服务
    }

    public function test()
    {
        $re = $this->_do();
        dump($re);
    }

    /**
     * 每个进程启动
     * @param $worker
     */
    public function onWorkerStart($worker)
    {
//
        $log = config('log');
        ini_set('memory_limit', '512M');

        $log['default'] = 'task';
        config($log, 'log');
        // 1秒一次轮询
        Timer::add(1, function () use ($worker) {
            $re = $this->_do();
            $this->output->writeln('worker:' . $worker->id . ':' . $re['message'] . ',code:' . $re['code'] . ',time:' . time_format());
        });
        // 1分钟一次轮询
        Timer::add(61, function () use ($worker) {
//            dump('minute');
            $tbk = new Tbk();
            $re = $tbk->getOrder(time(), 600);
            if ($re['code'] != 0) {
                $this->output->writeln('tb:order,time:' . time_format() . 'message' . $re['message']);
            } else {
                $this->output->writeln('tb:order,time:' . time_format());
            }
//            (time() % 3) == 0 && $jd->getOrder(3, time(), 1800);
        });
        // 1分钟一次轮询
        Timer::add(62, function () use ($worker) {
//            dump('minute');
            $tui = new Tui();
            $re = $tui->getBb();
            if ($re['code'] != 0) {
                $this->output->writeln('tui:getBb,time:' . time_format() . 'message' . $re['message']);
            } else {
                $this->output->writeln('tui:getBb,time:' . time_format());
            }
//            (time() % 3) == 0 && $jd->getOrder(3, time(), 1800);
        });
        // 1分钟一次轮询
        Timer::add(55, function () use ($worker) {
//            dump('minute');
            $pdd = new Pdd();;

            $re = $pdd->checkOkPromotion();
            if ($re['code'] != 0) {
                $this->output->writeln('pdd:Promotion,time:' . time_format() . 'message' . $re['message']);
            } else {
                $this->output->writeln('pdd:Promotion,time:' . time_format());
            }
        });
        // 1分钟一次轮询
        Timer::add(59, function () use ($worker) {
//            dump('minute');
            $jd = new JdEvent();
            $re = $jd->getOrder(3, time(), 1800);
            if ($re['code'] != 0) {
                $this->output->writeln('jd:order,time:' . time_format() . 'message' . $re['message']);
            } else {
                $this->output->writeln('jd:order,time:' . time_format());
            }
//            (time() % 3) == 0 && $jd->getOrder(3, time(), 1800);
        });

        // 2分钟一次轮询
        Timer::add(60, function () use ($worker) {
            $pdd = new PddEvent();
            $re = $pdd->getOrderByUpdate(time());
            if ($re['code'] != 0) {
                $this->output->writeln('pdd:order,time:' . time_format() . 'message' . $re['message']);
            } else {
                $this->output->writeln('pdd:order,time:' . time_format());
            }
        });
        // 10分钟一次轮询
        Timer::add(600, function () use ($worker) {
//            dump('minute')
            $this->output->writeln('worker:minute,time:' . time_format());
            $index = new ExchangeCouponEvent();
            $index->info();
        });
        // 5分钟一次轮询
        Timer::add(300, function () use ($worker) {
//            dump('minute')
            $event = new ShareEvent();
            $data = $event->autoBuild();

            $this->output->writeln('worker:5minute,message:' .  $data['message'] . ',code:' . $data['code'] . ',time:' . time_format());

        });
        // 1小时一次轮询
        Timer::add(3600, function () use ($worker) {
            $this->output->writeln('worker:hour,time:' . time_format());
            Log::write('3600');
//            dump('hour');
//            $e = new PosEvent();
//            //终端返现
//            $re = $e->payback();
//            if ($re['code'] != 0) {
//                $this->output->writeln('worker:' . $worker->id . ':' . $re['message'] . ',code:' . $re['code'] . ',time:' . time_format());
//            }
            $index = new ExchangeCouponEvent();
            $index->fail();
            $third = new ThirdRechargeEvent();
            $third->fail();;
            $po = new PointOrderEvent();
            $po->fail();
            //有效贡献考核
            UserEvent::f3BuserCheck();

            $order = new PosOrderEvent();
            $order->timeoutClose();
            $this->output->writeln('worker:hour end,time:' . time_format());
        });
        // 1小时一次轮询
        Timer::add(3601, function () use ($worker) {
            $tui = new Tui();
            $re = $tui->getOrders();
            $this->output->writeln('worker:hour2,time:' . time_format() . $re['code'] . 'message' . $re['message']);

        });
        // 1天一次轮询
        Timer::add(86400, function () use ($worker) {

            $this->output->writeln('worker:day,time:' . time_format());
            // 商铺交易信息重置
            StoreEvent::transInfo();

            // 代理结算
            BillEvent::agentMonth();
        });
    }


    /**
     * @return array
     */
    protected function _do(): array
    {
        try {
            $jobs = JobModel::where('status', 1)
                ->where('reserved', 1)
                ->whereTime('reserved_start_time', '<=', 'now')
                ->whereTime('reserved_end_time', '>', 'now')
                ->order('reserved_start_time ASC')->select();

            foreach ($jobs as $job) {
                //执行队列任务
//                if (true === $this->_checkFrequency($job)) {
//                    continue;
//                }
                if ($job['release']) {
                    // 重复执行任务
                    $param = $job['data'];
                } else {
                    // 单次任务
                    $task = $job->tasks()->where('available', 1)->order('reserved_time ASC')->whereTime('reserved_time', '<=', 'now')->find();
                    if (!$task) {
                        // 无记录。不执行
                        $job['reserved_end_time'] = time_format();
                        $job['reserved'] = 0;
                        $job->save();
                        continue;
                    }
                    $param = $task['data'];
                }
                if (false !== strpos($job['task'], '@')) {
                    $resolve = explode('@', $job['task']);
                    $obj = new $resolve[0]();
                    $method = $resolve[1];
                    $result = $obj->$method($param);
                } else {
                    $method = 'task' . Str::studly($job['task']) . 'Job';
                    if (method_exists($this, $method)) {
                        $result = $this->$method($param);
                    } else {
                        $result = 'no func';
                    }
                }
                if (!$job['release']) {
                    // 单次任务，查询下次执行时间
                    $nextTask = $job->tasks()->where('id', '<>', $task['id'])->where('available', 1)->order('reserved_time ASC')->find();
                    $task['available'] = 0;
                    $task['available_time'] = time_format();
                    $task->save();
                    if ($nextTask) {
                        $job['reserved_start_time'] = $nextTask['reserved_time'];
                    } else {
                        $job['reserved_end_time'] = time_format();
                        $job['reserved'] = 0;
                    }
                } else {
                    if (is_numeric($job['release'])) {
                        $job['reserved_start_time'] = time_format(time() + $job['release']);
                    } else {
                        $job['reserved_start_time'] = time_format($job['release']);
                    }
                    // 保存失败执行日志
                    if (isset($result['code']) && 0 != $result['code']) {
                        $task = [];
                        $task['result'] = $result;
                        $task['available'] = 0;
                        $task['reserved_time'] = time_format();
                        $task['available_time'] = time_format();
                        $job->tasks()->save($task);
                    }
                }
                $job['times'] = Db::raw('times+1');
                $job['available_time'] = time_format();
                $job->save();
                // 记录频次
//                if (isset($result['code'])) {
//                    $this->_TaskCount($job, 0 == $result['code'] ? 1 : 0, 0 == $result['code'] ? 0 : 1);
//                }
            }
            $data['code'] = 0;
            $data['message'] = 'runs：' . $jobs->count();
        } catch (\Exception $e) {
            $data['code'] = 1;
            $data['message'] = $e->getMessage();
        }
        return $data;
    }


    protected function _checkFrequency($job): bool
    {
        return $this->_checkFrequencyDeal($job, time_format(time(), 'YmdHi'), 'minute') ||
            $this->_checkFrequencyDeal($job, time_format(time(), 'YmdH'), 'hour') ||
            $this->_checkFrequencyDeal($job, time_format(time(), 'Ymd'), 'day');
    }

    protected function _checkFrequencyDeal($job, $tag, $field): bool
    {
        if ($job[$field] > 0) {
            // 每天限制
            $count = JobCountModel::where('job_id', $job['id'])->where('tag', $tag)->value('total');
            if ($count > $job[$field]) {
                return true;
            }
        }
        return false;
    }

    /**
     * 增加执行统计
     * @param $job
     * @param $success
     * @param $fail
     * @return void
     */
    protected function _TaskCount($job, $success, $fail)
    {
        //qps
        $cache_tag = 'task_count_qps_' . $job['id'] . '_' . time();
        if (Cache::has($cache_tag)) {
            Cache::dec($cache_tag);
        } else {
            Cache::set($cache_tag, 1, 1000);
        }
        $this->_TaskCountDeal($job, time_format(time(), 'YmdHi'), $success, $fail);
        $this->_TaskCountDeal($job, time_format(time(), 'YmdH'), $success, $fail);
        $this->_TaskCountDeal($job, time_format(time(), 'Ymd'), $success, $fail);
        $this->_TaskCountDeal($job, time_format(time(), 'Ym'), $success, $fail);
    }

    protected function _TaskCountDeal($job, $tag, $success, $fail)
    {
        $jobCount = JobCountModel::where('job_id', $job['id'])->where('tag', $tag)->find();
        if (!$jobCount) {
            $insert['job_id'] = $job['id'];
            $insert['tag'] = $tag;
            $insert['success'] = $success;
            $insert['fail'] = $fail;
            $insert['total'] = $fail + $success;
            JobCountModel::create($insert);
        } else {
            if ($success) {
                $jobCount['success'] = Db::raw('success+' . $success);
            } else {
                $jobCount['fail'] = Db::raw('fail+' . $fail);
            }
            $jobCount['total'] = Db::raw('total+' . ($success + $fail));
            $jobCount->save();
        }
    }

    /**
     * 断开设备
     * @param TcpConnection $connection
     */
    public function onClose($connection)
    {
    }

    public function onError($connection, $code, $msg)
    {
        Log::channel('socket')->error('msg ' . $msg . ' error ' . $code);
    }

}
